package decoder;

import eventloop.MessageEventLoopGroup;
import ffmpeg.EventDecoderRunnable;
import rtmp.*;
import sun.rmi.runtime.Log;
import user.Publish;
import user.PublishGroup;
import user.Receive;
import user.ReceiveGroup;
import util.Common;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoopGroup;
import io.netty.handler.codec.ByteToMessageDecoder;
import org.apache.log4j.Logger;

import java.util.*;

public class RtmpDecoder extends ByteToMessageDecoder {

    static Logger logger = Logger.getLogger(RtmpDecoder.class);

    //  chunk message 数据
    private ByteBuf byteBuf = null;


    private boolean isVod = false; // 是否是点播方式，没有推流端

    private int count = 0;

    //握手数据
    private RtmpHandshake rtmpHandshake = new RtmpHandshake();

    private String path = null;

    RtmpAudio rtmpAudio = new RtmpAudio();

    AudioStreamDecoder audioStreamDecoder = new AudioStreamDecoder();

    private RtmpPacket rtmpPacket = null;

    private RtmpPacket2 rtmpPacket2 = null;

    private boolean isPause = false;

    private int byteReadLength = 0;

    private int chunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;
    private int receiveChunkLength = Common.DEFAULT_CHUNK_MESSAGE_LENGTH;

    private EventLoopGroup eventLoopGroup;
    private MessageEventLoopGroup messageEventLoopGroup;
    private EventDecoderRunnable eventDecoderRunnable;

    public RtmpDecoder(EventLoopGroup eventLoopGroup, MessageEventLoopGroup messageEventLoopGroup){
        this.eventLoopGroup = eventLoopGroup;
        this.messageEventLoopGroup = messageEventLoopGroup;
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        byteBuf = in;
        if (!rtmpHandshake.isHandshake) { //rtmp 握手认证
            rtmpHandshake.handServerShake(ctx,in);
            if (rtmpHandshake.isHandshake) {
                RtmpResponse.responseChunkSize(ctx,Common.SERVER_RECEIVE_CHUNK_LENGTH,chunkLength);
                this.receiveChunkLength = Common.SERVER_RECEIVE_CHUNK_LENGTH;
            }
        } else {
            handChunkMessage(ctx);
        }
    }

    private void crateResponseRunnable(ChannelHandlerContext ctx) {
        this.eventDecoderRunnable = new EventDecoderRunnable(ctx, receiveChunkLength, "F:\\Work\\rustProject\\marvel-player\\test.mp4");
//        this.eventLoopGroup.execute(this.eventDecoderRunnable);
        this.eventDecoderRunnable.run();
    }

//      +-------+     +--------------+----------------+
//              | Chunk |  =  | Chunk Header |   Chunk Data   |
//      +-------+     +--------------+----------------+
//
//      +--------------+     +-------------+----------------+-------------------+
//              | Chunk Header |  =  | Basic header| Message Header |Extended Timestamp |
//      +--------------+     +-------------+----------------+-------------------+
//
    /**
     * 解析chunkMessage 数据
     *
     * @param ctx
     */
    private void handChunkMessage(final ChannelHandlerContext ctx) {


        if (rtmpPacket2 == null) {
            rtmpPacket2 = new RtmpPacket2();
        }

        while (byteBuf.readableBytes() > 0) {
            if (rtmpPacket2.decoder(byteBuf)) {
                switch (rtmpPacket2.getMessageType()) {
                    case ChunkSize:
                        RtmpChunkSize rtmpChunkSize = new RtmpChunkSize();
                        rtmpChunkSize.setChunkSize(this.chunkLength);
                        rtmpChunkSize.parseChunkSize(rtmpPacket2.getMessageData());
//                        RtmpResponse.responseChunkSize(ctx,rtmpChunkSize.getChunkSize(),rtmpChunkSize.getChunkSize());
                        rtmpPacket2.setChunkLength(rtmpChunkSize.getChunkSize());
                        chunkLength = rtmpChunkSize.getChunkSize();
                        break;
                    case BYTES_READ:
                        RtmpBytesRead rtmpBytesRead = new RtmpBytesRead();
                        rtmpBytesRead.setBytesRead(rtmpPacket2.getMessageData());
                        this.byteReadLength = rtmpBytesRead.getLen();
//                        logger.info("len = " + eventDecoderRunnable.getResponseCheckLength() + ", byteReadLen = " + rtmpBytesRead.getLen());

                        if (eventDecoderRunnable != null && isVod) {
                            logger.info("out size = " +ctx.channel().unsafe().outboundBuffer().size());
                            if (ctx.channel().unsafe().outboundBuffer().size() < 100 && !isPause) { //防止数据太大溢出
                                this.eventDecoderRunnable.run();

//                                crateResponseRunnable(ctx);
                            } else {
                                logger.info("消息太多");
                            }
                        }
                        break;
                    case PING:
                        RtmpPing rtmpPing = new RtmpPing();
                        rtmpPing.setPing(rtmpPacket2.getMessageData());
                        break;
                    case SERVER_WINDOW:
                        RtmpServerWindow rtmpServerWindow = new RtmpServerWindow();
                        rtmpServerWindow.setRtmpServerWindow(rtmpPacket2.getMessageData());
                        break;
                    case AUDIO:
                        rtmpAudio.setAudioData(rtmpPacket2.getMessageData(),this.path,rtmpPacket2.getRenderTimestamp(),audioStreamDecoder);
//                        messageEventLoopGroup.setAudioMessage(this.path, rtmpPacket.getMessageData(), rtmpPacket.getRenderTimestamp());
                        break;
                    case VIDEO:
                        RtmpVideo rtmpVideo = new RtmpVideo();
                        rtmpVideo.setVideoData(rtmpPacket2.getMessageData(),this.path,rtmpPacket2.getRenderTimestamp());
//                        messageEventLoopGroup.setVideoMessage(this.path, rtmpPacket.getMessageData(), rtmpPacket.getRenderTimestamp());
                        break;
                    case NOTIFY:
                        RtmpNotify rtmpNotify = new RtmpNotify();
                        rtmpNotify.setNotify(rtmpPacket2.getMessageData(),this.path);
                        if(rtmpNotify.getData() != null) {
                            Publish publish = PublishGroup.getChannel(path);
                            if (publish != null) {
                                publish.MetaData = rtmpNotify.getData();
                            }
                        }
                        break;
                    case Control:
                        RtmpControl rtmpControl = new RtmpControl();
                        rtmpControl.setControl(rtmpPacket2.getMessageData(),ctx,receiveChunkLength);
                        logger.info("strameId === " + rtmpPacket2.getStreamId());
                        if(rtmpControl.getMsg().equals("FCPublish")){
                            this.path = rtmpControl.getPath();
                            Publish client = new Publish();
                            client.path = path;
                            client.publish = ctx;
                            client.chunk_size = chunkLength; //客户端上来的 chunkLength 大小
//                            this.messageEventLoopGroup.setPublishChannel(this.path, client);
                            PublishGroup.setChannel(path, client);
                        } else if(rtmpControl.getMsg().equals("play")) {
                            if (this.isPause) {
                                isPause = false;
                                logger.info("播放吗");
                                this.eventDecoderRunnable.start();
                                this.eventDecoderRunnable.run();
                            } else {
                                this.path = rtmpControl.getPath();
                                Receive receive = new Receive();
                                receive.receive = ctx;
                                receive.playing = true;
                                receive.ready = false;
                                receive.keyframe = false;
                                receive.check_size = receiveChunkLength;
                                ReceiveGroup.setChannel(path, receive);
//                            this.messageEventLoopGroup.setReceiveChannel(this.path, receive);
                                rtmpPacket2.type = 1;
                                if (this.eventDecoderRunnable == null && isVod) {
                                    // 3 . 在 Runnable 中用户自定义耗时操作, 异步执行该操作, 该操作不能阻塞在此处执行
                                    crateResponseRunnable(ctx);
                                }
                            }
                        } else if (rtmpControl.getMsg().equals("connect")) {
                            this.isVod = rtmpControl.isVod();
                            logger.info("connect");
                        } else if (rtmpControl.getMsg().equals("FCUnpublish")) {
                            logger.info("推流结束");
                            ctx.close();
                        } else if (rtmpControl.getMsg().equals("seek")) {
                            if (isVod) {
                                this.eventDecoderRunnable.stop();
                                this.eventDecoderRunnable.setSeek(rtmpControl.getTime() * 1000);
                                this.eventDecoderRunnable.run();
                            }

                        } else if (rtmpControl.getMsg().equals("pause")) {
                            isPause = true;
                            if (isVod)
                                this.eventDecoderRunnable.stop();
                        }
                        break;
                    case NOT:
                        logger.info("消息类型还没编写");
                        RtmpNot rtmpNot = new RtmpNot();
                        rtmpNot.setNot(rtmpPacket2.getMessageData());
                        ctx.close();
                        byteBuf.release();
                        return;
                }

            } else {
                this.rtmpPacket2.release();
                break;
            }

            this.rtmpPacket2.release();
        }

//        do {
//
////        logger.info(ctx + " === "+this.byteBuf.readableBytes());
//            if(rtmpPacket != null && rtmpPacket.isLackMessage()) {
//                rtmpPacket.lackMessage(byteBuf);
//            } else if (rtmpPacket != null && rtmpPacket.getByteBuf() != null && rtmpPacket.getReadIndex() > 0 && rtmpPacket.getByteBuf().refCnt() > 0) {
//                rtmpPacket.reset(rtmpPacket.getByteBuf(), ctx);
//            } else if (rtmpPacket != null){
//                rtmpPacket.reset(byteBuf,ctx);
//            } else {
//                rtmpPacket = new RtmpPacket(ctx,byteBuf);
//            }
//
//            if(rtmpPacket.isLackMessage()){
//                return;
//            }
//
//            if(!rtmpPacket.isSubpackage()) {
//                switch (rtmpPacket.getMessageType()) {
//                    case ChunkSize:
//                        RtmpChunkSize rtmpChunkSize = new RtmpChunkSize();
//                        rtmpChunkSize.setChunkSize(this.chunkLength);
//                        rtmpChunkSize.parseChunkSize(rtmpPacket.getMessageData());
////                        RtmpResponse.responseChunkSize(ctx,rtmpChunkSize.getChunkSize(),rtmpChunkSize.getChunkSize());
//                        rtmpPacket.setChunkLength(rtmpChunkSize.getChunkSize());
//                        chunkLength = rtmpChunkSize.getChunkSize();
//                        break;
//                    case BYTES_READ:
//                        RtmpBytesRead rtmpBytesRead = new RtmpBytesRead();
//                        rtmpBytesRead.setBytesRead(rtmpPacket.getMessageData());
//                        this.byteReadLength = rtmpBytesRead.getLen();
//                        logger.info("len = " + eventDecoderRunnable.getResponseCheckLength() + ", byteReadLen = " + rtmpBytesRead.getLen());
//
//                        if (eventDecoderRunnable != null) {
//                            logger.info("out size = " +ctx.channel().unsafe().outboundBuffer().size());
//                            if (ctx.channel().unsafe().outboundBuffer().size() < 100 && !isPause) { //防止数据太大溢出
//                                this.eventDecoderRunnable.run();
//
////                                crateResponseRunnable(ctx);
//                            } else {
//                                logger.info("消息太多");
//                            }
//                        }
//                        break;
//                    case PING:
//                        RtmpPing rtmpPing = new RtmpPing();
//                        rtmpPing.setPing(rtmpPacket.getMessageData());
//                        break;
//                    case SERVER_WINDOW:
//                        RtmpServerWindow rtmpServerWindow = new RtmpServerWindow();
//                        rtmpServerWindow.setRtmpServerWindow(rtmpPacket.getMessageData());
//                        break;
//                    case AUDIO:
//                        rtmpAudio.setAudioData(rtmpPacket.getMessageData(),this.path,rtmpPacket.getRenderTimestamp(),audioStreamDecoder);
////                        messageEventLoopGroup.setAudioMessage(this.path, rtmpPacket.getMessageData(), rtmpPacket.getRenderTimestamp());
//                        break;
//                    case VIDEO:
//                        RtmpVideo rtmpVideo = new RtmpVideo();
//                        rtmpVideo.setVideoData(rtmpPacket.getMessageData(),this.path,rtmpPacket.getRenderTimestamp());
////                        messageEventLoopGroup.setVideoMessage(this.path, rtmpPacket.getMessageData(), rtmpPacket.getRenderTimestamp());
//                        break;
//                    case NOTIFY:
//                        RtmpNotify rtmpNotify = new RtmpNotify();
//                        rtmpNotify.setNotify(rtmpPacket.getMessageData(),this.path);
//                        if(rtmpNotify.getData() != null) {
//                            Publish publish = PublishGroup.getChannel(path);
//                            if (publish != null) {
//                                publish.MetaData = rtmpNotify.getData();
//                            }
//                        }
//                        break;
//                    case Control:
//                        RtmpControl rtmpControl = new RtmpControl();
//                        rtmpControl.setControl(rtmpPacket.getMessageData(),ctx,receiveChunkLength);
//                        logger.info("strameId === " + rtmpPacket.getStreamId());
//                        if(rtmpControl.getMsg().equals("FCPublish")){
//                            this.path = rtmpControl.getPath();
//                            Publish client = new Publish();
//                            client.path = path;
//                            client.publish = ctx;
//                            client.chunk_size = chunkLength; //客户端上来的 chunkLength 大小
////                            this.messageEventLoopGroup.setPublishChannel(this.path, client);
//                            PublishGroup.setChannel(path, client);
//                        } else if(rtmpControl.getMsg().equals("play")) {
//                            if (this.isPause) {
//                                isPause = false;
//                                logger.info("播放吗");
//                                this.eventDecoderRunnable.start();
//                                this.eventDecoderRunnable.run();
//                            } else {
//                                this.path = rtmpControl.getPath();
//                                Receive receive = new Receive();
//                                receive.receive = ctx;
//                                receive.playing = true;
//                                receive.ready = false;
//                                receive.keyframe = false;
//                                receive.check_size = receiveChunkLength;
//                                ReceiveGroup.setChannel(path, receive);
////                            this.messageEventLoopGroup.setReceiveChannel(this.path, receive);
//                                rtmpPacket.type = 1;
//                                if (this.eventDecoderRunnable == null) {
//                                    // 3 . 在 Runnable 中用户自定义耗时操作, 异步执行该操作, 该操作不能阻塞在此处执行
//                                    crateResponseRunnable(ctx);
//                                }
//                            }
//                        } else if (rtmpControl.getMsg().equals("connect")) {
//                            logger.info("connect");
//                        } else if (rtmpControl.getMsg().equals("FCUnpublish")) {
//                            logger.info("推流结束");
//                            ctx.close();
//                        } else if (rtmpControl.getMsg().equals("seek")) {
//                            this.eventDecoderRunnable.stop();
//                            this.eventDecoderRunnable.setSeek(rtmpControl.getTime() * 1000);
//                            this.eventDecoderRunnable.run();
//                        } else if (rtmpControl.getMsg().equals("pause")) {
//                            isPause = true;
//                            this.eventDecoderRunnable.stop();
//                        }
//                        break;
//                    case NOT:
//                        logger.info("消息类型还没编写");
//                        RtmpNot rtmpNot = new RtmpNot();
//                        rtmpNot.setNot(rtmpPacket.getMessageData());
//                        ctx.close();
//                        byteBuf.release();
//                        return;
//                }
//            }
//        } while (rtmpPacket.getReadIndex() > 0 && rtmpPacket.getByteBuf().refCnt() > 0);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.err.println("错误信息");
        logger.info(cause.getMessage());
        ctx.close();
        List<Receive> listVideo = ReceiveGroup.getChannel(this.path);
        List<Receive> newListVideo = new ArrayList<Receive>();
        for (int i = 0; i < listVideo.size(); i++) {
            Receive receive = listVideo.get(i);
            if (receive.receive != ctx) {
                newListVideo.add(receive);
            }
        }
        ReceiveGroup.setChannel(this.path, newListVideo);
        super.exceptionCaught(ctx, cause);
    }
}